package simplelab;

import java.awt.Point;
import java.util.ArrayList;
/*
 * The information about the tile's of the world is stored
 * in this class.
 */


//mi�rt nem l�p teljeset az elej�n

//rekurzi� a recombinate-re

//lefutattni 1-2 forg�s
//sz�m�t�si komplexit�s cs�kkent�se
//eredm�ny, fut�si id�

//ha mégse venné fel a kaját az ellenfél, akkor visszamenjen
public class World {
    public final static int deepness = 10;
    public boolean eredeti = false;
    public final static int worldWidth = 60;
    public final static int worldHeigth = 60;
    public final static int maxEatPerTurn = 200;
    public final static int foodLostPerTurn = 50;
    public final static int lowEnergy = 200;
    public final static int considerIntelAgeValid = 8;
    public final static int maxFoods = 4;
    public final static int foodlessTurnLimit = 30;
    public static int turnsSinceLastFoodAction = -120;
    public static int turn = -1; // The turn we are in, from our perception
    public Tile[][] tile;
    public int lastChangeTurn;
    public boolean[][] points = new boolean[60][60];
    public int lastEnemyPoint;
    public static DebugMap debugMap;
    public boolean enemyOnFood;
    public ArrayList<Agent> Friendlies;
    public ArrayList<Agent> Hostiles;
    public boolean myTeamIDSet;
    public int myTeamID;
    public int leaderID;
    public int tasksGiven;
    public int teamEnergy;
    public static Point position;
	public static ArrayList<Food> Foods;
    private boolean kettomely =false;
    private boolean guiOn = false;
    private boolean combo =true;
    public Task mainTask;
    Point[] mypos = new Point[deepness-2];
    public boolean[][][]  mytile= new boolean[deepness -2][worldWidth][worldHeigth];
	//public int onFood;
    // Defines wich checkPoint should the non-leader agents aim
   /* public int rotationState;
    public int timeBeforeRotation;
    public int timeSinceRotation;
*/
    public World() {
        tile = new Tile[worldWidth][worldHeigth];
        for (int x = 0; x < worldWidth; x++) {
            for (int y = 0; y < worldHeigth; y++) {
                tile[x][y] = new Tile();
            }
        }
        
        for (int k = 0; k < 60; k++){
                for (int l = 0; l < 60; l++){
                        points[k][l]=false;
                }
        }
        
        for(int i = 0; i< deepness-2;i++){
            mypos[i] = new Point();                        
        }
        lastChangeTurn = 0;
        debugMap = new DebugMap(this);
        /* Friendlies = new ArrayList<Agent>();
        Hostiles = new ArrayList<Agent>();*/
        Foods = new ArrayList<Food>();
        myTeamIDSet = false;
        myTeamID = -1;
        leaderID = 0;
        tasksGiven = 0;
       // onFood= -1;
        lastEnemyPoint = 20000;
        enemyOnFood = false;
        /*rotationState = 0;
        timeSinceRotation = -100;
        timeBeforeRotation = 40;
		*/
    }
	
    public static void error(){
        if(Foods.size() != 0){
            int temp =min(position);
            if(min(position)%1000==0){
                Foods.remove(temp/1000);
            }
        }
        
    }
    public static void errorBlock(){
        if(Foods.size() != 0){
            int temp =min(position);
           // if(manhattan(pos, Foods.get(temp)) ==1){
                Foods.remove(temp/1000);
            //}
        }
        
    }
    
    public void foodManages(Beliefs beliefs){
        //boolean notInside = false;
        foodDelete(beliefs.pos,beliefs.dir);
        //System.out.println("fooooood");
        for(int i = 0; i<beliefs.foods.size();i++){
            Foods.add(beliefs.foods.get(i));
            
        }
        /***for(int i = 0; i< Foods.size(); i++){
            System.out.println(Foods.get(i).position + " ffsdfdsfdsfdsffsdfwe2w3w ");
        }***/
    }
    public void foodDelete(Point pos, int dir){
        /****System.out.println(Foods.size() + " sddsfsfd "+ pos);
        for(int i = 0; i< Foods.size(); i++){
            System.out.println(Foods.get(i).position + " fff ");
        }***/
        int plus = 1, temp;
        if(dir==0 || dir == 3) plus= -1;
        for(int i = 0; i< Foods.size(); i++){
         
            if(dir%2==0) {
                temp = (Foods.get(i).position.y - pos.y)*plus; 
                if(temp>-1 && temp< 12 && Math.abs(Foods.get(i).position.x-pos.x) < temp+1   ){

                    ///System.out.println("0t�r�lve: "+ Foods.get(i).position.x + " y "+ Foods.get(i).position.y );
                    Foods.remove(i);
                    --i;
                }
            }else{
                temp = (Foods.get(i).position.x - pos.x)*plus; 
                if(temp>-1 && temp< 12 && Math.abs(Foods.get(i).position.y-pos.y) < temp+1 ){
                    ///System.out.println("1t�r�lve: "+ Foods.get(i).position.x + " y "+ Foods.get(i).position.y );
                    Foods.remove(i);
                    --i;
                }

            }
        }
        /***
        System.out.println(Foods.size() + " sddsfdsfdsdfdsfdsfdfdssfd "+ pos);
        System.out.println(Foods.size() + " qwewqefsfd "+ pos);
        for(int i = 0; i< Foods.size(); i++){
            System.out.println(Foods.get(i).position + " fff ");
        }***/
    }

    //visszaadja a legk�zelebbi kaja sorsz�m�t
    //ha a rajta �ll, akkor a sorsz�mmalával*1000 t�r vissza
    //egyégként pedig a távolság és a sorszám*1000-rel
    public static int min(Point pos){
        int temp = 0;
        int min= manhattan(pos, Foods.get(0).position);
        for(int i= 1;  i < Foods.size(); i++){
            if(manhattan(pos, Foods.get(i).position)<min){
                temp = i;
                min = manhattan(pos, Foods.get(i).position);
            }
        }
        if(min == 0){
        
            return temp*1000;
        }
        return (min + (temp*1000));
    }
    public Command Manage(Beliefs beliefs){
            /*int asdf=0;
            for(int i = 0; i< 60; i++)
                    for(int l = 0; l< 60; l++)
                            asdf+= tile[i][l].value;*/
        
        MyTeamID(beliefs.team);
        position= beliefs.pos;
        lastChangeTurn ++;
        //turn = beliefs.
        
        if(beliefs.energy<lowEnergy){
            return new Command(Command.Action.Wait,0,0);
        }
        
        teamEnergy =beliefs.energy;
        int enemyPoint = (int)(teamEnergy /beliefs.ratio) - teamEnergy;
        ///System.out.println(enemyPoint + " asd "+ lastEnemyPoint + " akdsjf�adsf�f�a "+ teamEnergy+ " ratio "+beliefs.ratio);
        if(enemyPoint>lastEnemyPoint+25 && !enemyOnFood){
            refreshTiles();
            /***System.out.println(" enemy");
            System.out.println(enemyPoint + " az most "+ lastEnemyPoint);***/
            enemyOnFood = true;
        }else enemyOnFood = false;
        
        lastEnemyPoint = enemyPoint;
            //ha maxfood+1 akkor kaj�l
            if( beliefs.foods.size()>0){
                    foodManages(beliefs);
                    //System.out.println(onFood +" d " + Foods.get(0).value);
                   
                    int temp = min(beliefs.pos);
                    if(temp%1000==0){
                        int real = temp/1000;
                        Foods.get(real).value -= maxEatPerTurn + foodLostPerTurn;
                        ///System.out.println(Foods.get(temp-100).value + " ennyi vazze "+  beliefs.foods.get(0).value);
                        
                        //kit�rli ha a k�r v�gi �tkez�s ut�n a kaja �rt�ke 1 al� cs�kkent
                  
                        if(Foods.get(real).value<1){
                            Foods.remove(real);
                            
                            ///System.out.println(" manage");
                            refreshTiles();
                        }
                        
                        return new Command(Command.Action.Eat,0,0);
                    }


            }
            //megkeresi a k�vetkez� kaj�t
            if( Foods.size()>0){                       
                    mainTask = findTarget(beliefs.pos,beliefs.agents,beliefs.dir);
            }else{
                    //ha az utolsó körben sikerült lefutnia egy combinatenek és még van pár lépés benne akkor nem változtatja
                    if(lastChangeTurn ==1 || lastChangeTurn > deepness/3){
                        OneTile(beliefs.pos, beliefs.dir);
                        mainTask = Combinate(beliefs.pos, beliefs.dir);
                        lastChangeTurn = 0;
                    }
            }


            if(!mainTask.task.isEmpty()){
                   
                    makeItNull(beliefs.pos,beliefs.dir,mainTask);
                    if(guiOn)
                        debugMap.mcanvas.paint(debugMap.mcanvas.getGraphics(),beliefs.pos,beliefs.dir, Foods);
                    return new Command((mainTask.task.get(0).x == 0)?
                            Command.Action.Step : (mainTask.task.get(0).x == 1)?Command.Action.Turn:Command.Action.Eat,
                            mainTask.task.get(0).y,0);
            }else
                    return new Command(Command.Action.Step,0,0);

    }
	
	/*public Task recursion(int deepness, int currentDeepness, ArrayList<int> attribute){
		for(int n = 0; n < 4; ++n){
			int[] temp = {i,l,j,k,n};
			tempTask = CalculateTile(pos, dir, temp);
			if(tempTask.profit> maxProfit.profit)
				maxProfit = tempTask;
		}
		
	}*/
      
    
        
        //kikeresi a legk�zelebbi kaj�t
    private Task findTarget(Point pos, ArrayList<Agent> enemy,int dir) {
        int onFood = min(pos);
       /*** System.out.println(Foods.size() + " asdf ");
        for(int i= 0; i < Foods.size();i++)
            System.out.println(Foods.get(i).position + " asfdsasdf ");***/
        int temp = onFood/1000;
        if(onFood %1000 ==0){
            
            Task ret= new Task();
            Foods.get(temp).value -= maxEatPerTurn + foodLostPerTurn;
            if(Foods.get(temp).value<1){
                Foods.remove(temp);
               /// System.out.println(" find target");
                refreshTiles();
            }

            ret.task.add(new Point(3,0));
            return ret;
        }
        return findPath(pos,Foods.get(temp),enemy,dir);
    }
    //meghat�rozza az utat a kaj�ig
    private Task findPath(Point pos, Food closest, ArrayList<Agent>  enemy, int dir) {
        Task ret= new Task();
        Point enemyPos= null;
        if(enemy != null && enemy.size()> 0){
            int n=0;
            for(int i = 0; i< enemy.size();i++){
                if(enemy.get(i).team != myTeamID){ 
                    enemyPos = enemy.get(i).position;
                    n=i;
                }
            }
            //System.out.println(" itt van " + enemy.size());
            if(min(enemyPos)%1000==0 ){
                for(int l = 0; l< Foods.size();l++){
                    if(enemyPos.x == Foods.get(l).position.x && enemyPos.y == Foods.get(l).position.y ){    
                        Foods.remove(l);
                        if(Foods.size()>0) return findTarget(pos, enemy,dir);
                        else return Combinate(pos, dir); 
                    }
                }
                
            }else if(manhattan(enemyPos, closest.position) < manhattan(pos, closest.position) && enemy.get(n).IsFacing(closest.position) ){
               // System.out.println(" törlés előtt "+Foods.size());
                Foods.remove(closest);
               // System.out.println(" törlés utn "+Foods.size());
                if(Foods.size()>0) return findTarget(pos, enemy,dir);
                else return Combinate(pos, dir); 
            }
            
        }
        Point tempPoint = null;
        if(closest.position.x > pos.x)
            tempPoint =new Point(0,1);
        else if(closest.position.x < pos.x)
            tempPoint =new Point(0,3);
        else if(closest.position.y > pos.y)
            tempPoint =new Point(0,2);
        else if(closest.position.y < pos.y)
            tempPoint =new Point(0,0);
        //else{
            //Foods.get(Foods.indexOf(closest)).value -= maxEatPerTurn;
            //ret.task.add(new Point(3,0));
        //}    
        if(tempPoint.y != dir){
            ret.task.add(new Point(1,tempPoint.y));
        }else{
            ret.task.add(tempPoint);
        }
        //ret.task.add(pos);

        return ret;
    }

    public static int manhattan(Point first, Point second){
        return Math.abs(first.x-second.x)+Math.abs(first.y-second.y);

    }
    
    
    //visszaadja a megadott l�p�sek sor�n �rintett mez�k �ssz�rt�k�t
    public Task CalculateTile(Point pos, int dir,  int[] actions){
            int plus;
            Point tempPos= new Point();
            tempPos.x = pos.x;
            tempPos.y = pos.y;
            Task returner = new Task();
            //megmondja hogy x tengelyre párhuzamos-e a dir
            boolean dirx;
            //segéd változók a az adott pont kiszámításához
            int tempX =0, tempY =0;

            for (int k = 0; k < deepness; k++){
                    /**
                     * actions: 
                     * 0 move forward
                     * 1 turn right
                     * 2 turn back
                     * 3 turn left
                     */
                if(actions[k]!=0){
                    if(actions[k] == 1) dir = (dir + actions[k])%4;  
                    else dir = (dir+3)%4; 
                    returner.task.add(new Point(1,dir));


                }
                //how the pos will change
                if(dir == 2 || dir == 1) plus = 1;
                else plus = -1;

                dirx = (dir % 2 == 0);

                //pos moving
                if(actions[k]==0){
                        if(dirx){
                                tempPos.y += plus;
                        }else{
                                tempPos.x += plus;
                        }
                        returner.task.add(new Point(0,dir));
                }

                //put the title
                for(int i = 1; i < 11; ++i){
                    if(dirx) tempY = tempPos.y + plus * i;
                    else             tempX = tempPos.x + plus * i;
                    for(int l = -1 * i; l <= i; ++l){
                        if(dirx) tempX = tempPos.x + l;
                        else tempY = tempPos.y + l;
                        if(tempX > -1 && tempX < worldWidth && tempY > -1 && tempY < worldHeigth )
                            points[tempX][tempY]=true;
                        //ha kifele néz else if()
                    }
                }

            }
            //bool t�mb, 
            for(int i =0; i<60 ;i++) {
                    for(int l =0; l<60 ;l++) {
                            if(points[i][l]){
                                    returner.profit += tile[i][l].value;
                                    points[i][l]=false;
                            }
                    }
            }
            return returner;
            
            
            
            
    }
    public void OneTile(Point pos, int dir){
        for (int d = 0; d < deepness-2; d++) {
            for (int x = 0; x < worldWidth; x++) {
                for (int y = 0; y < worldHeigth; y++) {
                    mytile[d][x][y] = false;
                }
            }
        }
        
        int plus;
        if(dir == 2 || dir == 1) plus = 1;
        else plus = -1;

        //System.out.println(mypos.length + " sdlfka" +pos+ " posx " +pos.x + " mypos "+ mypos+ " d " + mypos[0]);
        mypos[0].x = pos.x;
        mypos[0].y = pos.y;
        boolean dirx= (dir%2==0);
        for(int l = 1; l < deepness-2 ; l++){
            if(dirx){
                    mypos[l].y += plus;
            }else{
                    mypos[l].x += plus;
            }
            int tempY=0, tempX=0;

            for(int i = 1; i < 11; ++i){
                if(dirx) tempY = mypos[l].y + plus * i;
                else             tempX = mypos[l].x + plus * i;
                for(int n = -1 * i; n <= i; ++n){
                    if(dirx) tempX = mypos[n].x + n;
                    else tempY = mypos[n].y + n;
                    if(tempX > -1 && tempX < worldWidth && tempY > -1 && tempY < worldHeigth )
                        mytile[l][tempX][tempY]=true;
                    //ha kifele néz else if()
                }
            }
        }
                   
    }
    
    
//8 m�lys�g, el kell �rnie valameddig, minim�lis fordul�s
    public Task ReCombinate(int deep, Point pos, int dir, int[] temp){
            Task tempTask, maxProfit = new Task();


            //eredeti
            if(eredeti){
                if(deep == deepness){
                        Point posTemp = new Point();
                        posTemp.x = pos.x;
                        posTemp.y = pos.y;
                        tempTask = CalculateTile(posTemp, dir, temp);
                        if(tempTask.profit> maxProfit.profit)
                                maxProfit = tempTask;

                }else {
                        for(int i = 0; i < 4; ++i){
                                int[] tempDeep = new int[deep+1];
                                if(temp !=null)
                                    System.arraycopy(temp, 0, tempDeep, 0, deep);
                                
                                
                                tempDeep[deep] = i; 

                                tempTask = ReCombinate(deep+1, pos, dir,tempDeep);
                                if(tempTask.profit > maxProfit.profit){
                                        maxProfit = tempTask;
                                        //System.out.println(maxProfit.profit+" "+deep+ " "+deepness+ " "+tempDeep[0] );
                                }


                        }
                }
            }else{
                
                int[] tempDeep = new int[deepness];
                for(int i = 0; i < deepness; ++i){
                    tempDeep[i]= 0;
                }
                maxProfit = CalculateTile(pos,dir,tempDeep);
                for(int i = deepness-2; i>-1 ;--i){

                    for(int n = 1; n<3; ++n){
                        tempDeep[i]=n;
                        tempTask = CalculateTile(pos,dir,tempDeep);	
                        if(tempTask.profit > maxProfit.profit){
                                maxProfit = tempTask;
                                   /* System.out.println(maxProfit.profit+" "+deep+ " "+deepness+ " "+tempDeep[0] );
                                    for(int k = 0; k<deepness;++k){
                                        System.out.print(tempDeep[k]+ " ");
                                    }
                                    System.out.println();*/
                        }
                    }
                    tempDeep[i]=0;
                }
                 if(kettomely){   
                    for(int i = deepness-2; i>-1 ;--i){

                        for(int n = 1; n<3; ++n){
                            tempDeep[i]=n; 
                            for(int l = deepness-1; l>i;--l){
                            for(int j = 1; j<3; ++j){
                                tempDeep[l]=j;
                                tempTask = CalculateTile(pos,dir,tempDeep);	
                                if(tempTask.profit > maxProfit.profit){
                                        maxProfit = tempTask;
                                       /* System.out.println(maxProfit.profit+" "+deep+ " "+deepness+ " "+tempDeep[0] );
                                        for(int k = 0; k<deepness;++k){
                                            System.out.print(tempDeep[k]+ " ");
                                        }
                                        System.out.println();*/
                                }
                                /*System.out.println("          "  + tempTask.profit);
                                 for(int k = 0; k<deepness;++k){
                                            System.out.print(tempDeep[k]+ " ");
                                        }
                                        System.out.println();*/
                            }
                            tempDeep[l]=0;
                            }

                        }
                        tempDeep[i]=0;
                    }
                }
            }

                ///System.out.println(pos+ " hopp� ");
            //}
            return maxProfit;
    }

    public Task Combinate(Point pos, int dir){
            Task maxProfit,tempTask;
            maxProfit = new Task();

            maxProfit = ReCombinate(0,pos,dir,null);
            if(maxProfit.profit ==0){
                for(int i=0; i< worldHeigth;i++){
                    for(int l=0; l< worldWidth;l++){
                        tile[i][l].value = 4;
                    }
                }
            }
            maxProfit = ReCombinate(0,pos,dir,null);
            return maxProfit;
    }
    
    //kinull�zza a l�tott mez�k �rt�k�t
    public void makeItNull(Point pos, int dir, Task maxProfit){
            int plus;

            // turning x = 1, y =  the dir
            if(maxProfit.task.get(0).x == 1 ){
                    dir = maxProfit.task.get(0).y;  
            }
            //how the pos will change
            if((dir == 2 || dir == 1)) plus = 1;
            else plus = -1;

            //plus *= -1;

            //pos moving
            tile[pos.x][pos.y].value=0;
            /*if(maxProfit.task.get(0).x==0){
                    if(dir % 2 == 0){
                            pos.y += plus;
                    }else{
                            pos.x += plus;
                    }
            }*/

            int tempX, tempY;

            //az �rt�ke ahol �llok 0
            

            for(int i = 1; i < 11; ++i){
                    for(int l = -1 * i; l <= i; ++l){
                            if(dir % 2 == 0){
                                    tempX = pos.x + l;
                                    tempY = pos.y + plus * i;
                            }else{
                                    tempX = pos.x + plus * i;
                                    tempY = pos.y + l;
                            }
                            //System.out.println(tempX+ "po" + tempY + "asdfdsa"+ pos.x+ " "+pos.y +" " + plus);
                            if(tempX > -1 && tempX < worldWidth && tempY > -1 && tempY < worldHeigth )
                                    tile[tempX][tempY].value=0;

                    }
            }
    }

    

    private void refreshTiles() {
        lastChangeTurn = 0;
        for(int i=0; i<60 ;i++){
            for(int l=0; l<60 ;l++){
                if(tile[i][l].value<4){
                    tile[i][l].value++;
                                }
            }
        }
}
		
	
	
	
	
	
   

    public void MyTeamID(int myTeamID) {
        if (myTeamIDSet == false) {
            this.myTeamID = myTeamID;
            myTeamIDSet = true;
        }
    }

    public void ManageHostileAgent(Agent agent) {
        // Is it really hostile?
        if (agent.team != myTeamID) {
            // Is agent on list?
            int i;
            for (i = 0; i < Hostiles.size() && Hostiles.get(i).id != agent.id; i++);
            if (i == Hostiles.size()) {
                Hostiles.add(agent);
            } else {
                Hostiles.get(i).RefreshIntel(agent);
            }
        }
    }

    /*public Agent ManageFriendlyAgent(Agent agent) {
        // Is it really friendly?
        if (agent.team == myTeamID) {
            // Is this the first agent?
            if (Friendlies.isEmpty()) {
                Friendlies.add(agent);
                leaderID = agent.id;
                AddInitialTask(agent);
            } else {
                // Is agent on list?
                int i;
                for (i = 0; i < Friendlies.size() && Friendlies.get(i).id != agent.id; i++);
                if (i == Friendlies.size()) {
                    Friendlies.add(agent);
                    AddInitialTask(agent);
                } else {
                    Friendlies.get(i).RefreshIntel(agent);
                    agent = Friendlies.get(i);
                }
            }
            // Manage Tile info
            if (tile[agent.position.x][agent.position.y].isVisited() == false
                    && agent.inWater) {
                // Setting water hassle
                for (WorldIterator it = new WorldIterator(this, agent.position.x, agent.position.y,
                            1, true, 1); it.IsNotEnd(); it.Next()) {
                        tile[it.x][it.y].setWaterHassle(
                                Heur.Manhattan(it.x, it.y, agent.position.x, agent.position.y));
                    }
                // Recalculate checkpoints, based on new hassle values
                CalculateCheckpoints();
                // Setting water probability
                if (Heur.Manhattan(agent.position, agent.lastPosition) == 1) {
                    int dx = agent.position.x - agent.lastPosition.x,
                            dy = agent.position.y - agent.lastPosition.y;
                    if (IsValidTile(agent.position.x + dx, agent.position.y + dy)) {
                        if (tile[agent.lastPosition.x][agent.lastPosition.y].isWater) {
                            tile[agent.position.x + dx][agent.position.y + dy].addWaterProbability(0.5);
                        } else {
                            tile[agent.position.x + dx][agent.position.y + dy].addWaterProbability(1.0);
                        }
                    }
                    if (IsValidTile(agent.position.x + dy, agent.position.y + dx)) {
                        tile[agent.position.x + dy][agent.position.y + dx].addWaterProbability(0.5);
                    }
                    if (IsValidTile(agent.position.x - dy, agent.position.y - dx)) {
                        tile[agent.position.x - dy][agent.position.y - dx].addWaterProbability(0.5);
                    }
                } else {
                    // We were spawned in water, or pushed to water
                    for (WorldIterator it = new WorldIterator(this, agent.position.x, agent.position.y,
                            1, false, 1); it.IsNotEnd(); it.Next()) {
                        tile[it.x][it.y].addWaterProbability(0.3);
                    }
                }
            }
            // Refresh seen tiles
            Point p = new Point();
            for (WorldIterator it = new WorldIterator(this, agent.position.x, agent.position.y, 10,
                    true, 0); it.IsNotEnd(); it.Next()) {
                p.setLocation(it.x, it.y);
                if (agent.CanSee(p)) {
                    // Get enemy on that tile
                    Agent enemy = null;
                    for (int i = 0; i < Hostiles.size(); i++) {
                        if (Hostiles.get(i).position.equals(p)) {
                            enemy = Hostiles.get(i);
                        }
                    }
                    tile[it.x][it.y].Refresh(enemy);
                }
            }
            // Visit this tile
            tile[agent.position.x][agent.position.y].Visit(agent.inWater);
            // If this is the leader, increase turns
            if (agent.id == leaderID) {
                Turn();
            }
            // Movement related stuff
            if (agent.lastPosition.equals(agent.position)) {
                agent.timeSpentHere++;
            } else {
                // We were attacked!
                if (Heur.Manhattan(agent.lastPosition, agent.position) > 1) {
                    agent.GetTask().attacked++;
                }
                agent.timeSpentHere = 0;
                agent.turnsMade = 0;
                agent.waited = 0;
            }
            agent.lastPosition.setLocation(agent.position);
        }
        return agent;
    }

    

    public void AddInitialTask(Agent myAgent) {
        ExploreTask exploreTask = new ExploreTask(this, checkPoints.get(tasksGiven));
        exploreTask.assignedTo = myAgent;
        myAgent.tasks.add(exploreTask);
        tasksGiven++;
        if (tasksGiven > 1) {
            tasksGiven++;
        }
    }

    public void ValidateTasks() {
        // Clear invalid tasks from agents
        Agent myAgent = null;
        Task task = null;
        for (int i = 0; i < Friendlies.size(); i++) {
            myAgent = Friendlies.get(i);
            for (int k = 0; k < myAgent.tasks.size(); k++) {
                task = myAgent.tasks.get(k);
                if (task.IsValid() == false) {
                    myAgent.tasks.remove(task);
                    k--;
                }
            }
        }
    }
    
    public void MakeNewTasks() {
        // Search for eat tasks
        EatTask eatTask = null;
        while ((eatTask = EatTask.MakeNew(this)) != null) {
            AssignTask(eatTask);
        }
        // Search for heal tasks
        HealTask healTask = null;
        while ((healTask = HealTask.MakeNew(this)) != null) {
            AssignTask(healTask);
        }
        // Search for check tasks (we need only 1)
        CheckTask checkTask = CheckTask.MakeNew(this);
        if (checkTask != null) {
            AssignTask(checkTask);
        }
    }
    
    public void AssignTask(Task task) {
        Agent idealAgent = null;
        int lowestAvailability = -1, currentAvailability;
        for (int i = 0; i < Friendlies.size(); i++) {
            currentAvailability = task.AvailabilityFrom(Friendlies.get(i));
            if (lowestAvailability == -1 || currentAvailability < lowestAvailability) {
                lowestAvailability = currentAvailability;
                idealAgent = Friendlies.get(i);
            }
        }
        idealAgent.tasks.add(task); 
        task.assignedTo = idealAgent;
    }
       
   

    
    // Get direction required to look, from two points next to each other
    public int ToDir(Point from, Point to) {
        int x = to.x - from.x, y = from.y - to.y;
        if (y == 1) {
            return 0;
        } else if (x == 1) {
            return 1;
        } else if (y == -1) {
            return 2;
        }
        return 3;        
    }
    
    public Command Approach(Agent myAgent, Point goal) {
        // Am I blocked?
        if (IsTileFree(myAgent.position) == false) {
            myAgent.goalBlocked = false;
            myAgent.isBlocked = true;
            // Make the agent wait for a while
            WaitTask task = new WaitTask(this, 10, 30);
            task.assignedTo = myAgent;
            myAgent.tasks.add(task);
            return new Command(Command.Action.Wait, 0, 0);            
        } else if (IsTileFree(goal) == false) {
            // Is Goal blocked?
            myAgent.goalBlocked = true;
            myAgent.isBlocked = false;
            return new Command(Command.Action.Wait, 0, 0);            
        }
        myAgent.goalBlocked = false;
        myAgent.isBlocked = false;
        
        // Astar to build a route, with goal 'n'
        Node n = astar.NextStep(myAgent.position, goal);
        if (n == null) {
            // Routing error
            return new Command(Command.Action.Wait, 0, 0);
        }
        boolean dontTurn = false;

        while (n.reachedFrom.reachedFrom != null) {
            n = n.reachedFrom;
        }
        if (World.turn - tile[n.point.x][n.point.y].lastCheck <= 2) {
            dontTurn = true;
        }
        int desiredDirection = ToDir(myAgent.position, n.point);

        // Checking if turning is accurate for goal
        if (dontTurn == false && myAgent.direction != desiredDirection) {
            return new Command(Command.Action.Turn, desiredDirection, 0);
        }
        // Approaching goal
        return new Command(Command.Action.Step, desiredDirection, 0);
    }

    public Agent IsThereFriendly(Point position) {
        for (int j = 0; j < Friendlies.size(); j++) {
            if (Friendlies.get(j).position.equals(position)) {
                return Friendlies.get(j);
            }
        }
        return null;
    }

    public static int CalculateEdgeHassle(int x, int y) {
        int minEdgeDistance = Math.min(worldWidth - 1 - x,
                worldHeigth - 1 - y);
        minEdgeDistance = Math.min(minEdgeDistance, x);
        minEdgeDistance = Math.min(minEdgeDistance, y);
        if (minEdgeDistance < 10) {
            //return 10 - minEdgeDistance;
            return 5;
        }
        return 0;
    }

    public boolean IsRotationReady() {
        // We already know all the food, or the last rotation was recently
        if (Foods.size() >= 2 || timeSinceRotation < timeBeforeRotation) {
            return false;
        }
        int numberOfReadyAgents = 0;
        Agent agent = null;
        // Start from 1: skip the leader
        for (int i = 1; i < Friendlies.size(); i++) {
            agent = Friendlies.get(i);
            if (agent.turnsMade > 4 && agent.GetTask().priority == 1
                    && Heur.Bird(agent.position, agent.GetTask().goal) <= 1) {
                numberOfReadyAgents++;
            }
        }
        if (numberOfReadyAgents >= UsefulAgents() - 2) {
            timeSinceRotation = 0;
            return true;
        }
        return false;
    }

    public int TeamEnergy() {
        int teamEnergy = 0;
        for (int i = 0; i < Friendlies.size(); i++) {
            teamEnergy += Friendlies.get(i).energy;
        }
        return teamEnergy;
    }

    private int UsefulAgents() {
        int useful = 0;
        for (int i = 0; i < Friendlies.size(); i++) {
            if (Friendlies.get(i).IsUseful()) {
                useful++;
            }
        }
        return useful;
    }

    private void Turn() {
        turn++;
        timeSinceRotation++;
        turnsSinceLastFoodAction++;
    }

    public Point AttackResult(Agent myAgent, Point pos) {
        Point result = (Point) pos.clone();
        Point attack = myAgent.AttackVector();
        result.setLocation(result.x + attack.x, result.y + attack.y);
        while (IsValidTile(result.x, result.y) && IsThereFriendly(result) == null
                && Heur.Manhattan(result, pos) <= 10) {
            result.setLocation(result.x + attack.x, result.y + attack.y);
        }
        result.setLocation(result.x - attack.x, result.y - attack.y);
        return result;
    }

    public boolean IsThereEnemy(Point pos) {
        Tile t = tile[pos.x][pos.y];
        if (t.lastEnemySeenHere == null) {
            return false;
        }
        if (t.lastEnemySeenHere.position.equals(pos)) {
            return World.turn - t.enemyWasHere <= considerIntelAgeValid;
        } else {
            t.lastEnemySeenHere = null;
            return false;
        }
    }

    public Agent GetEnemy(Point pos) {
        if (IsThereEnemy(pos)) {
            return tile[pos.x][pos.y].lastEnemySeenHere;
        }
        return null;
    }

    public static boolean IsValidTile(int x, int y) {
        if (x < 0 || x > worldWidth - 1 || y < 0 || y > worldHeigth - 1) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        String friendInfo = new String();
        for (int i = 0; i < Friendlies.size(); i++) {
            friendInfo += "    ID: " + Friendlies.get(i).id
                    + "      Energy: " + Friendlies.get(i).energy
                    + "\n        TaskPri: " + Friendlies.get(i).GetTask().priority
                    + "  goal: " + Friendlies.get(i).GetTask().goal.x + "," 
                    + Friendlies.get(i).GetTask().goal.y + "\n";
        }        
        return "myTeamIDSet: " + myTeamIDSet + "\n"
                + "leaderID: " + leaderID + "\n"
                + "tasksGiven: " + tasksGiven + "\n"
                + "Friendlies: " + Friendlies.size() + "\n"
                + friendInfo
                + "Hostiles: " + Hostiles.size() + "\n"
                + "Foods: " + Foods.size() + "\n"
                + "\n" + "TeamEnergy: " + TeamEnergy() + "\n"
                + "\n" + "teamTime: " + Knowledge.myTeamTimeLeft + "\n"
                + "turn: " + turn + "\n"
                + "lastEat: " + World.turnsSinceLastFoodAction + "\n";
    }*/

   

    
}
